Bitmap的加载和Cache(一)— Bitmap的高效加载

前言

准备写一个系列,来逐步实现一个ImageLoader。图片加载一直都是Android知识体系的重点,因为内存的限制,我们要对图片进行压缩,防止OOM。又因为网络的限制,我们需要对图片进行缓存,避免无所谓的流量消耗。图片的显示实际上都是bitmap实现的,所以本篇文章主要讲解怎么高效加载一个Bitmap。

一般加载

在介绍Bitmap的高效加载之前,我们先说一下如何加载一个Bitmap。Bitmap在Android中指的是一张图片,可以是png也可以是jpg等其他图片格式。那么如何加载一张图片呢?BitmapFactory类提供了四类方法:decodeFile、decodeResource、decodeStream、decodeArray。分别支持从文件系统、资源、输入流和字节数组加载出一个Bitmap对象,其中decodeFile和decodeResource又是间接调用了decodeStream方法,这四类方法最终是在Android底层实现的。

高效加载

那么如何高效的加载图片呢?核心思想也很简单,那就是采用BitmapFactory.Options来加载所需尺寸的图片。这里假设通过ImageView来显示图片,很多时候ImageView并没有图片的原始尺寸那么大,这个时候把原图直接加载进来显然有点多余,因为ImageView并没有办法显示原始照片。这时候可以通过BitmapFactory.Options就可以按一定采样率来加载缩小后的图片,将压缩后的图片设置给ImageView,这样就会降低内存占用从而在一定程度上避免OOM,提高了BItmap加载时的性能。BitmapFactory提供的加载图片的四种方法都支持BitmapFactory.Option参数,通过它就可以很方便的对一张图片进行采样缩放。

通过BitmapFactory.Options来缩放图片,主要是用到了它的inSampleSize参数,即采样率。当inSampleSize为1时,采样后的图片为图片的原始大小;当inSampleSize为2时,那么采样后的图片其宽和高均为原图大小的1/2,而像素数为原图的1/4,其占用内存也为原图的1/4。拿一张1024 1024像素的图片来说,假定采用ARGB8888格式存储,那么它占用的内存为 1024 1024 * 4,即4M,如果inSampleSize为2,那么采样后的的图片占用内存只有1M。可以看出,只有当inSampleSize大于1才会有缩放效果。有一种特殊情况,当inSampleSize小于1时,其作用相当于1,即无缩放效果。inSampleSize的取值应该总是2的指数,比如1、2、4、8、16等等。如果外界传递inSampleSize的值不是2的指数,那么系统会向下取整并选择一个最接近2的指数来替代。

四个步骤

  1. BitmapFactory.Options的inJustDecodeBounds参数设置为true并加载图片。
  2. 从BitmapFactory.Options中取出图片原始的宽高,它们对应outWidth和outHeight参数。
  3. 根据实际所需大小计算出inSimpleSize。
  4. 将BitmapFactory.Options的inJustDecodeBounds参数设置为false并重新加载图片。

这里的inJustDecodeBounds参数设置为true时,BitmapFactory只会解析图片原始的宽高信息,并不会真正的去加载图片,所以这个操作是轻量级的。另外需要注意的是,这个BitmapFactory获取的图片的宽高和图片的位置以及程序的设备有关。比如同一张图片放在不同的drawable目录下或者程序运行在不同屏幕密度的设备上,都有可能导致BitmapFactory获取到不同的结果。

代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
public static Bitmap decodeSampleBitmap(Resources res, int resId, int reqWidth, int reqHeight) {
final BitmapFactory.Options options = new BitmapFactory.Options();
//第一步
options.inJustDecodeBounds = true;
BitmapFactory.decodeResource(res, resId, options);
//第三步
options.inSampleSize = calcInSimpleSize(options, reqWidth, reqHeight);
//第四步
options.inJustDecodeBounds = false;
return BitmapFactory.decodeResource(res, resId, options);
}
public static int calcInSimpleSize(BitmapFactory.Options options, int reqWidth, int reqHeight) {
//第二步
final int height = options.outHeight;
final int width = options.outWidth;
int inSimpleSize = 1;
if (height > reqHeight || width > reqWidth) {
final int halfHeight = height / 2;
final int halfWidth = width / 2;
while ((halfHeight / inSimpleSize) >= reqHeight && (halfWidth / width) >= reqWidth) {
inSimpleSize = inSimpleSize * 2;
}
}
return inSimpleSize;
}
我们一直都向往,面朝大海,春暖花开。 但是几人能做到,心中有爱,四季不败?